package com.booter.webflux.controller;

import com.booter.webflux.model.Menu;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * Mono 常用的方法有：
 * <p>
 * Mono.create()：使用 MonoSink 来创建 Mono。
 * Mono.justOrEmpty()：从一个 Optional 对象或 null 对象中创建 Mono。
 * Mono.error()：创建一个只包含错误消息的 Mono。
 * Mono.never()：创建一个不包含任何消息通知的 Mono。
 * Mono.delay()：在指定的延迟时间之后，创建一个 Mono，产生数字 0 作为唯一值。
 * </p>
 */

@RestController
public class ConsumerController {

    @RequestMapping(value = "client/menu/list")
    public Flux<Menu> list1() {
        WebClient client = WebClient.create("http://localhost:8080/menu/list");
        return client.post().retrieve().bodyToFlux(new ParameterizedTypeReference<Menu>() {
        });
    }

    @RequestMapping(value = "client/menu/list2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Menu> list2() {
        WebClient client = WebClient.create("http://localhost:8080/menu/list");
        return client.post().retrieve().bodyToFlux(new ParameterizedTypeReference<Menu>() {
        });
    }

    @RequestMapping(value = "client/menu/list3", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Menu> list3() {
        WebClient client = WebClient.create("http://localhost:8080/menu/list2");
        return client.post().retrieve().bodyToFlux(new ParameterizedTypeReference<Menu>() {
        });
    }


    @RequestMapping("rest/menu/list")
    public String restList() {
        RestTemplate template = new RestTemplate();
        return template.getForObject("http://localhost:8080/menu/list", String.class);
    }

    @RequestMapping("rest/menu/list2")
    public String restList2() {
        RestTemplate template = new RestTemplate();
        return template.getForObject("http://localhost:8080/menu/list2", String.class);
    }


    @RequestMapping(value = "client/menu/save")
    public Mono<Menu> save(@RequestBody Mono<Menu> menu) {
        WebClient client = WebClient.create("http://localhost:8080/menu/save");
//        return client.post().body(new Publisher<Menu>() {
//            @Override
//            public void subscribe(Subscriber<? super Menu> s) {
//                s.onNext(menu);
//            }
//        }, Menu.class).retrieve().bodyToMono(Menu.class);
        return client.post().body(menu, Menu.class).retrieve().bodyToMono(Menu.class);
    }


    @RequestMapping("client/menu/{id}")
    public Flux<Menu> getById(@PathVariable String id) {
        WebClient client = WebClient.create("http://localhost:8080/menu/{id}");
        return client.post().uri(uriBuilder -> uriBuilder.build(id)).retrieve().bodyToFlux(Menu.class);


    }

    @RequestMapping("client/menu/id")
    public Flux<Menu> getById2(@RequestParam String id) {
        WebClient client = WebClient.create("http://localhost:8080");
        return client.post().uri(uriBuilder -> uriBuilder.path("/menu/id").queryParam("id", id).build()).retrieve().bodyToFlux(Menu.class);
    }


    @RequestMapping(value = "client/menus/{ids}")
    public Flux<Menu> getbyId3(@PathVariable List<String> ids, ServerHttpRequest request) {
        WebClient client = WebClient.create("http://localhost:8080");
        return client.post().uri(uriBuilder -> uriBuilder.path("menu/ids").queryParam("ids", ids).build()).retrieve().bodyToFlux(Menu.class);
    }


    @RequestMapping(value = "client/menus")
    public Flux<Menu> getbyId4(@RequestParam List<String> ids, ServerHttpRequest request) {
        WebClient client = WebClient.create("http://localhost:8080/menus/{ids}");

        return client.post().uri(uriBuilder -> uriBuilder.build(ids))
                .retrieve()
                //根据状态码区分异常
                .onStatus(httpStatus -> httpStatus.is4xxClientError(), clientResponse -> {
                    return Mono.error(new RuntimeException(clientResponse.statusCode() + ",error"));
                })
                .onStatus(httpStatus -> httpStatus.is5xxServerError(), clientResponse -> {
                    return Mono.error(new RuntimeException(clientResponse.statusCode() + ",error"));
                })
                .bodyToFlux(Menu.class);
    }


    @RequestMapping(value = "client/menus2")
    public Flux<Menu> getbyId5(@RequestParam List<String> ids, ServerHttpRequest request) {
        WebClient client = WebClient.builder().baseUrl("http://localhost:8080/menus/{ids}")
                .filter(new ExchangeFilterFunction() {
                    @Override
                    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {

                        return null;
                    }

                    @Override
                    public ExchangeFilterFunction andThen(ExchangeFilterFunction afterFilter) {
                        return null;
                    }

                    @Override
                    public ExchangeFunction apply(ExchangeFunction exchange) {
                        return null;
                    }
                })
                .build();
        return client.post().uri(uriBuilder -> uriBuilder.build(ids))
                .retrieve()
                //根据状态码区分异常
                .onStatus(httpStatus -> httpStatus.is4xxClientError(), clientResponse -> {
                    return Mono.error(new RuntimeException(clientResponse.statusCode() + ",error"));
                })
                .onStatus(httpStatus -> httpStatus.is5xxServerError(), clientResponse -> {
                    return Mono.error(new RuntimeException(clientResponse.statusCode() + ",error"));
                })
                .bodyToFlux(Menu.class);
    }


}
